/*
   Copyright (C) 2009 Klaus Blindert <klaus.blindert@web.de>

   Part of the libABF project.

   libABF is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "abf_format.h"
#ifdef ABF_HAVE_LIBXML
#    include "abf_format_xml.h"
#endif
#include "abf_format_dat.h"
#include "abf_util.h"

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>

/**
 * @file abf_format.c
 * @author Klaus Blindert <klaus.blindert@web.de>
 *
 * @brief Read/write data files.
 */

struct abf_format
{
    const char* Name;
    abf_status (*Read)(abf_flat_t* flat, const char* filename);
    abf_status (*Write)(abf_flat_t* flat, const char* filename);
};

abf_status abf_can_read(const char* filename)
{
    struct stat st;
    if (stat(filename,&st) != 0)
        return ABF_E_NOTFOUND;
    if (S_ISDIR(st.st_mode))
        return ABF_E_NOTFOUND;
    if (access(filename, R_OK))
        return ABF_E_CANNOT_READ;
    return ABF_OK;
}

abf_status abf_can_write(const char* filename)
{
    struct stat st;
    if (stat(filename,&st) == -1)
        return ABF_E_NOTFOUND;
    if (S_ISDIR(st.st_mode))
        return ABF_E_NOTFOUND;
    if (access(filename, W_OK))
        return ABF_E_CANNOT_WRITE;
    return ABF_OK;
}

abf_status abf_read_any(abf_flat_t* flat, const char* name)
{
    CHECK_FLAT(flat);
    assert(name);
    if (ABF_OK == abf_load (abf_getformat(ABF_F_DAT), flat, name))
        return ABF_OK;
    if (ABF_OK == abf_load (abf_getformat(ABF_F_XML), flat, name))
        return ABF_OK;
    return ABF_E_PARSER_READ_ERROR;
}

abf_format_t abf_formats[] =
{
    { "dat", Read:abf_read_dat, Write:abf_write_dat },
    { "any", Read:abf_read_any, Write:abf_write_dat },
#ifdef ABF_HAVE_LIBXML
    { "xml", Read:abf_read_xml, Write:abf_write_xml },
#endif
};

abf_format_t* abf_getformat(const char* name)
{
    int i;
    assert(name);
    for (i=0; i!=sizeof(abf_formats)/sizeof(abf_formats[0]);++i)
        if (strcmp(name,abf_formats[i].Name) == 0)
            return &abf_formats[i];
    fprintf(stderr, "*** format '%s' not found.\n",name);
    return &abf_formats[0];
}

abf_status abf_load(const abf_format_t* format, abf_flat_t* flat, const char* filename)
{
    CHECK_FLAT(flat);
    assert(filename);
    assert(format);

    abf_status status = abf_can_read(filename);
    if (status!=ABF_OK)
        return status;

    return format->Read (flat, filename);
}

abf_status abf_save(const abf_format_t* format, abf_flat_t* flat, const char* filename)
{
    CHECK_FLAT(flat);
    assert(filename);
    assert(format);

    abf_status status = abf_can_write(filename);
    if (status!=ABF_OK)
        return status;

    return format->Write (flat, filename);
}
